"""
插件沙箱环境
限制插件对系统资源的访问，提供安全的执行环境
"""

import os
import sys
import tempfile
import threading
from typing import Any, Dict, Optional
from .permissions import PermissionManager, AccessControl, Permission


class SandboxEnvironment:
    """沙箱环境"""
    
    def __init__(self, plugin_name: str, permission_manager: PermissionManager):
        self.plugin_name = plugin_name
        self.permission_manager = permission_manager
        self.access_control = AccessControl(permission_manager)
        
        # 沙箱资源限制
        self._resource_limits = {
            'max_files': 100,
            'max_memory': 100 * 1024 * 1024,  # 100MB
            'max_execution_time': 30  # 30秒
        }
        
        # 沙箱文件系统
        self._sandbox_root = None
        self._file_handles = {}
        self._file_count = 0
        
        self._setup_sandbox()
    
    def _setup_sandbox(self):
        """设置沙箱环境"""
        # 创建沙箱根目录
        self._sandbox_root = tempfile.mkdtemp(prefix=f"plugin_{self.plugin_name}_")
        
        # 限制系统调用
        self._restricted_functions = {
            'os.system': self._restricted_system,
            'os.popen': self._restricted_popen,
            'exec': self._restricted_exec,
            'eval': self._restricted_eval
        }
    
    def _restricted_system(self, command):
        """限制系统调用"""
        raise PermissionError(f"Plugin '{self.plugin_name}' is not allowed to execute system commands")
    
    def _restricted_popen(self, command, *args, **kwargs):
        """限制进程创建"""
        raise PermissionError(f"Plugin '{self.plugin_name}' is not allowed to create subprocesses")
    
    def _restricted_exec(self, code, *args, **kwargs):
        """限制exec调用"""
        raise PermissionError(f"Plugin '{self.plugin_name}' is not allowed to execute code dynamically")
    
    def _restricted_eval(self, expression, *args, **kwargs):
        """限制eval调用"""
        raise PermissionError(f"Plugin '{self.plugin_name}' is not allowed to evaluate expressions")
    
    # 文件系统访问控制
    def open_file(self, path: str, mode: str = 'r'):
        """安全的文件打开"""
        if not self.access_control.check_access(self.plugin_name, Permission.FILE_READ):
            if 'w' in mode or 'a' in mode:
                if not self.access_control.check_access(self.plugin_name, Permission.FILE_WRITE):
                    raise PermissionError(f"Plugin '{self.plugin_name}' lacks file write permission")
            else:
                raise PermissionError(f"Plugin '{self.plugin_name}' lacks file read permission")
        
        # 限制文件数量
        if self._file_count >= self._resource_limits['max_files']:
            raise ResourceWarning(f"Plugin '{self.plugin_name}' exceeded maximum file handles")
        
        # 限制文件访问范围
        if not self._is_path_allowed(path):
            raise PermissionError(f"Plugin '{self.plugin_name}' is not allowed to access path: {path}")
        
        # 在沙箱目录中打开文件
        sandbox_path = self._map_to_sandbox(path)
        file_handle = open(sandbox_path, mode)
        self._file_handles[id(file_handle)] = sandbox_path
        self._file_count += 1
        
        return file_handle
    
    def _is_path_allowed(self, path: str) -> bool:
        """检查路径是否被允许"""
        # 只允许访问沙箱目录和临时目录
        abs_path = os.path.abspath(path)
        sandbox_root = os.path.abspath(self._sandbox_root)
        temp_dir = tempfile.gettempdir()
        
        return (abs_path.startswith(sandbox_root) or 
                abs_path.startswith(temp_dir) or
                abs_path.startswith(os.path.abspath('.')))
    
    def _map_to_sandbox(self, path: str) -> str:
        """将路径映射到沙箱目录"""
        if os.path.isabs(path):
            # 绝对路径映射到沙箱目录
            filename = os.path.basename(path)
            return os.path.join(self._sandbox_root, filename)
        else:
            # 相对路径保持相对
            return os.path.join(self._sandbox_root, path)
    
    def close_file(self, file_handle):
        """关闭文件"""
        if id(file_handle) in self._file_handles:
            file_handle.close()
            del self._file_handles[id(file_handle)]
            self._file_count -= 1
    
    def get_resource_usage(self) -> Dict[str, Any]:
        """获取资源使用情况"""
        return {
            'file_handles': self._file_count,
            'sandbox_root': self._sandbox_root,
            'resource_limits': self._resource_limits.copy()
        }
    
    def cleanup(self):
        """清理沙箱环境"""
        # 关闭所有文件
        for file_id in list(self._file_handles.keys()):
            try:
                file_handle = next((fh for fh in self._file_handles.values() if id(fh) == file_id), None)
                if file_handle:
                    file_handle.close()
            except:
                pass
        
        self._file_handles.clear()
        self._file_count = 0
        
        # 清理沙箱目录
        if self._sandbox_root and os.path.exists(self._sandbox_root):
            import shutil
            try:
                shutil.rmtree(self._sandbox_root)
            except:
                pass


class SandboxManager:
    """沙箱管理器"""
    
    def __init__(self, permission_manager: PermissionManager):
        self.permission_manager = permission_manager
        self._sandboxes: Dict[str, SandboxEnvironment] = {}
        self._lock = threading.Lock()
    
    def create_sandbox(self, plugin_name: str) -> SandboxEnvironment:
        """为插件创建沙箱环境"""
        with self._lock:
            if plugin_name not in self._sandboxes:
                self._sandboxes[plugin_name] = SandboxEnvironment(plugin_name, self.permission_manager)
            return self._sandboxes[plugin_name]
    
    def get_sandbox(self, plugin_name: str) -> Optional[SandboxEnvironment]:
        """获取插件的沙箱环境"""
        return self._sandboxes.get(plugin_name)
    
    def destroy_sandbox(self, plugin_name: str):
        """销毁插件的沙箱环境"""
        with self._lock:
            if plugin_name in self._sandboxes:
                self._sandboxes[plugin_name].cleanup()
                del self._sandboxes[plugin_name]
    
    def cleanup_all(self):
        """清理所有沙箱环境"""
        with self._lock:
            for sandbox in self._sandboxes.values():
                sandbox.cleanup()
            self._sandboxes.clear()